package com.kepler.social.web.controller;

import com.kepler.social.app.port.UserPasswordResetPort;
import com.kepler.social.common.constants.BusinessState;
import com.kepler.social.common.constants.Enums;
import com.kepler.social.common.constants.TipsConst;
import com.kepler.social.common.utils.Safes;
import com.kepler.social.common.utils.ValidateAssert;
import com.kepler.social.generic.SecurityHelper;
import com.kepler.social.service.vo.LoginUser;
import com.kepler.social.service.vo.SysUserVo;
import com.kepler.social.app.port.UserLoginPort;
import com.kepler.social.app.port.UserPasswordChangePort;
import com.kepler.social.domain.port.vo.LoginSuccessUserInfoVo;
import com.kepler.social.web.common.HttpResult;
import com.kepler.social.web.common.LoginBody;
import com.kepler.social.web.comp.SysLoginService;
import com.kepler.social.web.comp.TokenService;
import com.kepler.social.web.controller.assembler.UserLoginResAssembler;
import com.kepler.social.web.controller.param.LoginUserInfoRes;
import com.kepler.social.web.controller.param.RetrievePasswordReq;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/sec")
@Slf4j
public class SecurityController {

    @Resource
    private SysLoginService loginService;
    @Resource
    private TokenService tokenService;
    @Resource
    private UserLoginPort userLoginPort;
    @Resource
    private UserPasswordResetPort userPasswordResetPort;
    @Resource
    private UserPasswordChangePort userPasswordChangePort;

    /**
     * 用户状态数据
     * 用户的更新状态，可以放入redis缓存，现在量少，所以直接读库
     * TODO 应不用的Case，待抽离
     *
     * @return
     */
    @RequestMapping(value = "/user-modified-check", method = RequestMethod.GET)
    public HttpResult userState(HttpServletRequest request, Long modifiedTime) {
        LoginUser loginUser = tokenService.getLoginUser(request);
        ValidateAssert.notNull(loginUser, TipsConst.not_login);
        LoginSuccessUserInfoVo mergeInfoVo = userLoginPort.loginSuccessUserInfo(loginUser.getUser().getId());
        LoginUserInfoRes res = mergeInfoVo.getModifiedTime() > Safes.of(modifiedTime, 0L)
                ? UserLoginResAssembler.userStateCheckInfoRes(mergeInfoVo)
                : null;
        return HttpResult.success(res);
    }

    /**
     * 账号密码登录，包括邮件和手机号码userSecurity
     * todo 待优化 如有多个途径登录，应该有多个port支持。特定的port完成鉴权，认证，登录等操作，返回数据给controller或者Rpc。loginService在这里有点
     * todo 网关服务的概念。正确的操作，应该是将所有的操作让如loginService中，UserLoginResAssembler也应让入loginService同级。
     * todo 目前保证核心业务架构，其他的不用着急优化，等业务复杂之后再说。否则为了架构而架构，反而适得其反。
     *
     * @param loginBody
     * @return
     */
    @PostMapping("sso-login")
    public HttpResult ssoLogin(@RequestBody LoginBody loginBody) {
        String token = loginService.login(loginBody.getAccount(), loginBody.getPassword(),
                loginBody.getMobilePhoneNumber(), loginBody.getCaptcha());
        log.info("login success {} {}", loginBody.getAccount(), loginBody.getMobilePhoneNumber());
        LoginUser loginUser = tokenService.getLoginUser(token);
        LoginSuccessUserInfoVo successUserInfoVo = userLoginPort.loginSuccessUserInfo(loginUser.getUser().getId());
        LoginUserInfoRes res = UserLoginResAssembler.loginUserInfoRes(token, successUserInfoVo);
        return new HttpResult(BusinessState.OK.code, TipsConst.login_in_success, res);
    }

    /**
     * 重置密码
     * 优先发送到手机上
     * 重置密码和找回密码 其实
     * @return
     */
    @PostMapping("reset-password")
    public HttpResult resetPassword() {
        SysUserVo userVo = SecurityHelper.getSysUserVo();
        ValidateAssert.notNull(userVo, TipsConst.not_login);
        Enums.TargetEnum targetEnum = userPasswordResetPort.resetPassword(userVo.getId());
        return new HttpResult(
                BusinessState.OK.code,
                targetEnum == Enums.TargetEnum.MOBILE_PHONE
                        ? TipsConst.mobile_phone_password_reset_success
                        : TipsConst.email_password_reset_success
        );
    }

    /**
     * 密码找回 发送验证码
     * 用户首先选择找回的方式 是邮件还是手机
     * 如果是邮件，生成验证码到邮件，并提示用户
     * 如果是手机，生成验证码到邮件，并提示用户
     * 用户输入验证码之后，如果正确，APP跳转到设置密码的界面进行修改
     *
     * @param req
     * @return
     */
    // TODO 按照单一职责原则 需要分成两个接口
    @PostMapping("retrieve-password/send-captcha")
    public HttpResult retrievePasswordSendCaptcha(@RequestBody RetrievePasswordReq req) {
        if (StringUtils.isNoneBlank(req.getEmail())) {
            userPasswordChangePort.sendRetrievePasswordEmailCaptcha(req.getEmail());
            return new HttpResult(BusinessState.OK.code, TipsConst.email_captcha_sent);
        } else {
            userPasswordChangePort.sendRetrievePasswordMobilePhoneCaptcha(req.getCountryCallingCode(), req.getMobilePhoneNumber());
            return new HttpResult(BusinessState.OK.code, TipsConst.mobile_phone_captcha_sent);
        }
    }

    /**
     * 密码找回 修改密码
     *
     * @param req
     * @return
     */
    @PostMapping("retrieve-password/modify")
    public HttpResult retrieveRasswordModify(@RequestBody RetrievePasswordReq req) {
        if (StringUtils.isNoneBlank(req.getEmail())) {
            userPasswordChangePort.changePasswordByEmail(req.getEmail(), req.getCaptcha(), req.getPassword());
            return new HttpResult(BusinessState.OK.code, TipsConst.email_captcha_sent);
        } else {
            userPasswordChangePort.changePasswordByMobilePhone(req.getCountryCallingCode(), req.getMobilePhoneNumber(),
                    req.getCaptcha(), req.getPassword());
        }
        // 删除用户缓存记录
        tokenService.delLoginUser(SecurityHelper.getLoginUser().getToken());
        return new HttpResult(BusinessState.OK.code, TipsConst.password_change_success);
    }

}
